package com.iteaj.iot.server.udp;

import com.iteaj.iot.FrameworkManager;
import com.iteaj.iot.config.ConnectProperties;
import com.iteaj.iot.event.ClientStatus;
import com.iteaj.iot.event.StatusEvent;
import io.netty.channel.ChannelDuplexHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.util.AttributeKey;
import io.netty.util.concurrent.ScheduledFuture;
import io.netty.util.internal.StringUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;

public class UdpEventManagerHandler extends ChannelDuplexHandler {

    private UdpServerComponent serverComponent;
    private Logger logger = LoggerFactory.getLogger(getClass());
    private Map<String, UdpIdleState> idleStateMap = new ConcurrentHashMap<>(128);
    private AttributeKey<Map<String, UdpIdleState>> attributeKey = AttributeKey.newInstance("UDP:DEVICE:MANAGER");

    public UdpEventManagerHandler(UdpServerComponent serverComponent) {
        this.serverComponent = serverComponent;
    }

    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        ctx.channel().attr(attributeKey).setIfAbsent(idleStateMap);
        super.channelActive(ctx);
    }

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        if(msg instanceof UdpServerMessage) {
            UdpServerMessage message = (UdpServerMessage) msg;
            String equipCode = message.getHead().getEquipCode();
            if(!StringUtil.isNullOrEmpty(equipCode)) {
                ConnectProperties config = serverComponent.config();
                UdpIdleState idleState = idleStateMap.get(equipCode);

                // 设备还没注册
                if(idleState == null) {
                    idleState = new UdpIdleState(equipCode, System.currentTimeMillis(), config, message.getSender());
                    idleStateMap.put(equipCode, idleState);

                    if(config.getAllIdleTime() > 0) {
                        ScheduledFuture<?> schedule = ctx.executor()
                                .schedule(new IdleTask(idleState, ctx), config.getAllIdleTime(), TimeUnit.SECONDS);
                        idleState.setScheduledFuture(schedule);
                    }

                    if(logger.isDebugEnabled()) {
                        logger.debug("客户端上线({}) - 客户端编号: {} - 客户端地址: {}", serverComponent.getName(), equipCode, message.getSender());
                    }

                    //触发设备上线事件
                    FrameworkManager.publishEvent(new StatusEvent(equipCode, ClientStatus.online, serverComponent));
                } else {
                    // 重新更新发送地址
                    idleState.setAddress(message.getSender());
                    long currentTimeMillis = System.currentTimeMillis();
                    ScheduledFuture future = idleState.getScheduledFuture();
                    if(future != null) { // 定时任务没执行直接取消
                        future.cancel(true);
                    }

                    idleState.setRegisterOfMills(currentTimeMillis);
                    ScheduledFuture<?> schedule = ctx.executor()
                            .schedule(new IdleTask(idleState, ctx), config.getAllIdleTime(), TimeUnit.SECONDS);
                    idleState.setScheduledFuture(schedule);
                }
            }
        }

        ctx.fireChannelRead(msg);
    }

    @Override
    public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
        if(evt instanceof UdpIdleState) {
            UdpIdleState remove = idleStateMap.remove(((UdpIdleState) evt).getEquipCode());
            if(logger.isDebugEnabled() && remove != null) {
                logger.debug("客户端超时下线({}) - 客户端编号: {} - 客户端地址: {} - 状态: 下线成功"
                        , serverComponent.getName(), remove.getEquipCode(), remove.getAddress());
            }

            if(remove != null) {
                //触发设备下线事件
                FrameworkManager.publishEvent(new StatusEvent(remove.getEquipCode(), ClientStatus.offline, serverComponent));
            }
        }

        super.userEventTriggered(ctx, evt);
    }

    protected class IdleTask implements Runnable {

        private UdpIdleState idleState;
        private ChannelHandlerContext ctx;

        public IdleTask(UdpIdleState idleState, ChannelHandlerContext ctx) {
            this.ctx = ctx;
            this.idleState = idleState;
        }

        @Override
        public void run() {
            try {
                idleState.setScheduledFuture(null);
                UdpEventManagerHandler.this.userEventTriggered(this.ctx, idleState);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
}
